package com.gitee.cliveyuan.tools.mail;

import com.gitee.cliveyuan.tools.Assert;
import com.gitee.cliveyuan.tools.CollectionTools;
import com.gitee.cliveyuan.tools.DateTimeTools;
import com.gitee.cliveyuan.tools.StringTools;
import com.gitee.cliveyuan.tools.exception.EmailException;
import com.google.common.collect.Lists;
import com.sun.mail.util.MailConnectException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.*;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.security.Security;
import java.util.List;
import java.util.Properties;

/**
 * 邮件发送工具
 * <p>
 * Created by clive at 2018/07/23.
 * Rebuilt by clive at 2018/11/26.
 *
 * @since 2.0.0
 */
public class EmailTools {

    private static final Logger logger = LoggerFactory.getLogger(EmailTools.class);

    private EmailTools() {
    }

    private String host;
    private String account;
    private String password;
    private List<String> recipients = Lists.newArrayList();
    private List<String> carbonCopies = Lists.newArrayList();
    private String subject;
    private String content;
    private List<File> attachments = Lists.newArrayList();
    private boolean isSSL;


    // region public methods

    /**
     * 构建EmailTools
     *
     */
    public static EmailTools build() {
        return new EmailTools();
    }


    /**
     * 发送邮件
     *
     * @throws EmailException
     */
    public void send() throws EmailException {

        //验证
        validate();

        //判断发送类型
        if (isSSL)
            ssl();
        else
            normal();
    }


    /**
     * 发送邮件主机
     * 如: smtp.qq.com; smtp.126.com;
     *
     * @param host
     */
    public EmailTools host(String host) {
        this.host = host;
        return this;
    }

    /**
     * 发件人邮箱账号
     * (xxx@qq.com;xxx@126.com)
     *
     * @param account
     */
    public EmailTools account(String account) {
        this.account = account;
        return this;
    }

    /**
     * 发件人邮箱密码
     *
     * @param password
     */
    public EmailTools password(String password) {
        this.password = password;
        return this;
    }

    /**
     * 收件人邮箱列表
     *
     * @param recipients
     */
    public EmailTools recipients(List<String> recipients) {
        this.recipients = recipients;
        return this;
    }

    /**
     * 添加收件人邮箱地址
     *
     * @param recipient
     */
    public EmailTools addRecipient(String recipient) {
        this.recipients.add(recipient);
        return this;
    }

    /**
     * 抄送人邮箱地址
     *
     * @param carbonCopies
     */
    public EmailTools carbonCopies(List<String> carbonCopies) {
        this.carbonCopies = carbonCopies;
        return this;
    }

    /**
     * 添加抄送人邮箱地址
     *
     * @param carbonCopie
     */
    public EmailTools addCarbonCopie(String carbonCopie) {
        this.carbonCopies.add(carbonCopie);
        return this;
    }

    /**
     * 邮箱主题
     *
     * @param subject
     */
    public EmailTools subject(String subject) {
        this.subject = subject;
        return this;
    }

    /**
     * 邮件内容
     *
     * @param content
     */
    public EmailTools content(String content) {
        this.content = content;
        return this;
    }

    /**
     * 附件文件列表
     *
     * @param attachments
     */
    public EmailTools attachments(List<File> attachments) {
        this.attachments = attachments;
        return this;
    }

    /**
     * 添加附件文件
     *
     * @param attachment
     */
    public EmailTools addAttachment(File attachment) {
        this.attachments.add(attachment);
        return this;
    }

    /**
     * 通过SSL发送
     *
     * @param SSL
     */
    public EmailTools isSSL(boolean SSL) {
        isSSL = SSL;
        return this;
    }

    // endregion public methods


    // region private methods

    private void normal() throws EmailException {
        try {
            Properties props = new Properties();
            // 发信的主机
            props.put("mail.smtp.host", host);
            props.put("mail.smtp.auth", "true");
            Session session = Session.getInstance(props);
            // 调试开关
            session.setDebug(false);
            MimeMessage message = new MimeMessage(session);

            // 给消息对象设置发件人/收件人/主题/发信时间
            InternetAddress from = new InternetAddress(account);
            message.setFrom(from);
            config(message);
            message.saveChanges();
            try (Transport transport = session.getTransport("smtp")) {
                transport.connect(host, account, password);
                transport.sendMessage(message, message.getAllRecipients());
            }
        } catch (MailConnectException e) {
            throw EmailException.connectFail(e.getMessage());
        } catch (AuthenticationFailedException e) {
            throw EmailException.authFail();
        } catch (SendFailedException e) {
            throw EmailException.invalidAddress();
        } catch (Exception e) {
            logger.error("send Exception", e);
            throw EmailException.unKnowError();
        }
    }

    private void ssl() throws EmailException {
        try {
            // 设置SSL连接、邮件环境
            Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
            final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
            Properties props = System.getProperties();
            props.setProperty("mail.smtp.host", host);
            props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
            props.setProperty("mail.smtp.socketFactory.fallback", "false");
            props.setProperty("mail.smtp.port", "465");
            props.setProperty("mail.smtp.socketFactory.port", "465");
            props.setProperty("mail.smtp.auth", "true");
            props.setProperty("mail.smtp.ssl.enable", "true");
            // 建立邮件会话
            Session session = Session.getDefaultInstance(props, new Authenticator() {
                // 身份认证
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(account, password);
                }
            });
            session.setDebug(false);
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(account));
            config(message);
            message.saveChanges();
            Transport.send(message);
        } catch (MailConnectException e) {
            throw EmailException.connectFail(e.getMessage());
        } catch (AuthenticationFailedException e) {
            throw EmailException.authFail();
        } catch (SendFailedException e) {
            throw EmailException.invalidAddress();
        } catch (Exception e) {
            logger.error("sendBySSL Exception", e);
            throw EmailException.unKnowError();
        }
    }

    private void validate() {
        Assert.notEmpty(host, "host can't be empty");
        Assert.notEmpty(account, "account can't be empty");
        Assert.notEmpty(password, "password can't be empty");
        Assert.notEmpty(recipients, "recipients can't be empty");
        Assert.notEmpty(subject, "subject can't be empty");
        Assert.notEmpty(content, "content can't be empty");
    }

    private void config(MimeMessage message) throws MessagingException, UnsupportedEncodingException {
        // 收件人
        for (String recipient : recipients) {
            if (StringTools.isNotBlank(recipient)) {
                InternetAddress to = new InternetAddress(recipient);
                message.addRecipient(Message.RecipientType.TO, to);
            }
        }

        // 抄送
        if (CollectionTools.isNotEmpty(carbonCopies)) {
            for (String carbonCopy : carbonCopies) {
                if (StringTools.isNotBlank(carbonCopy)) {
                    message.addRecipient(Message.RecipientType.CC, new InternetAddress(carbonCopy));
                }
            }
        }

        message.setSubject(subject);
        message.setSentDate(DateTimeTools.now());
        BodyPart mbp = new MimeBodyPart();
        mbp.setContent(content, "text/html;charset=utf-8");
        Multipart mm = new MimeMultipart();
        mm.addBodyPart(mbp);
        message.setContent(mm);

        if (CollectionTools.isNotEmpty(attachments)) {
            for (File attachment : attachments) {
                mbp = new MimeBodyPart();
                DataSource source = new FileDataSource(attachment);
                mbp.setDataHandler(new DataHandler(source));
                mbp.setFileName(MimeUtility.encodeWord(attachment.getName()));
                mm.addBodyPart(mbp);
            }
        }
    }
    // endregion private methods

}
